﻿using System;
using System.Collections.Generic;
using System.Reflection;
using MLSharp.Utilities;

namespace MLSharp.Filters
{
	/// <summary>
	/// Helper class that loads filters based on string specifications.
	/// </summary>
	public static class FilterLoader
	{
		#region Private Fields

		/// <summary>
		/// The look-up from filter name to type.
		/// </summary>
		private static IDictionary<string,Type> mFilterMap;

		#endregion

		#region Constructors

		/// <summary>
		/// Loads the filters from the current assembly.
		/// </summary>
		static FilterLoader()
		{
			mFilterMap = new Dictionary<string, Type>();

			foreach (Type t in Assembly.GetExecutingAssembly().GetExportedTypes())
			{
				if (t.GetInterface(typeof(IDataSetFilter).Name) != null &&
					!t.IsAbstract &&
					!t.IsInterface)
				{
					string name = GetNormalizedName(t.Name);
					mFilterMap.Add(name, t);
				}
			}
		}

		#endregion

		/// <summary>
		/// Normalizes the name to lower-case and appends "filter" if it doesn't
		/// already end in "filter".
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		private static string GetNormalizedName(string name)
		{
			name = name.ToLower();

			if (!name.EndsWith("filter"))
			{
				name += "filter";
			}

			return name;
		}

		/// <summary>
		/// Attempts to load all of the specified filters.
		/// </summary>
		/// <param name="filterArgs"></param>
		/// <returns></returns>
		public static IDataSetFilter[] LoadAll(string[] filterArgs)
		{
			IDataSetFilter[] filters = new IDataSetFilter[filterArgs.Length];

			for (int i=0; i < filters.Length; i++)
			{
				filters[i] = Load(filterArgs[i]);
			}

			return filters;
		}

		/// <summary>
		/// Attempts to loads the specified filter.
		/// </summary>
		/// <param name="filterArg"></param>
		/// <returns></returns>
		/// <exception cref="FilterNotFoundException">Thrown if the specified filter can't be found.</exception>
		public static IDataSetFilter Load(string filterArg)
		{
			//Parse the argument string
			string[] parts = filterArg.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);

			string name = GetNormalizedName(parts[0]);

			if (!mFilterMap.ContainsKey(name))
			{
				throw new FilterNotFoundException(parts[0]);
			}

			//Load the filter
			IDataSetFilter filter = Reflector.CreateInstance<IDataSetFilter>(mFilterMap[name]);

			for (int i=1; i < parts.Length; i++)
			{
				//Assignments look like Property:value[,value[...]]
				string[] assignment = parts[i].Split(':');

				Reflector.SetPropertyValue(filter, assignment[0], assignment[1]);
			}

			return filter;
		}
	}
}
